feat(types,mobile)!: retire the MobileResponsiveConfig and GestureConfig published types - #7683
Conversation
…fig published types Both were declared in packages/types/src/mobile.ts and published twice -- re-exported by the @object-ui/types root barrel and by @object-ui/mobile's -- and each had exactly one consumer: the `responsive` / `gestures` member of MobileComponentConfig, which objectui#5942 (PR #7526) retired. Re-measured on origin/main a472b07 before deleting: each name was a declaration plus two barrel re-exports and nothing else (read-shape probes over all tracked files, controls GestureType 36 / GestureContext 7 / ResponsiveValue 11 lit in the same run; objectstack sibling checkout 0 hits for both, control ComponentSchema 270; no package.json in that repo depends on @object-ui/*). Same route the container took: removed outright, not tombstoned. The whole interface goes, so there is no surviving object to hang a `?: never` key on; no documentation ever taught either; and there is no Zod mirror at all -- packages/types/src/zod/ has never had a mobile twin -- so the "deletion under a non-strict z.object is a silent strip" argument has no schema to apply to and the refusal is type-level only (TS2305 / TS2724 at the import), the objectui#7654 shape. Both barrels drop the names; a `//` RETIRED note replaces each declaration (line comments are stripped from declaration emit, the objectui#5942 contract-review lesson), and the surviving JSDoc on GestureType and the useSpecGesture hook no longer names the retired symbol, so both names occur zero times across every emitted .d.ts (rebuilt and measured; control GestureContext 2 / 3). The page-nav-misc-spec-parity rows that pinned the two names' spec non-ownership retire with the types: a name this package no longer exports cannot collide with anything, so a pin on it would guard nothing. The absence is pinned instead in mobile-residue-retired-7519.test.ts on the tsc channel (`@ts-expect-error` through the import('...') spelling, each paired with a live control through the same spelling) and by a source read of both barrels. Changeset: minor for @object-ui/types and @object-ui/mobile, graded on the published-surface change -- the in-repo consumer count is zero, consumers outside this repository are not visible from here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
…ired type The RETIRED notes and the changeset said the two-prong discriminator did not apply; the PM's mid-task correction (objectui#7678) asks for the measurement per key instead. Prong 1 (named live replacement key): none for either -- MobileResponsiveConfig's distinctive keys have zero readers outside the declaration, no dispatcher reads a gesture `action`, and TouchInteraction.gestures is SpecGestureConfig[], a different contract. Prong 2 (advertised as working): the only CHANGELOG lines are the objectstack#4115 rename-ledger rows (6 / 6, replicated per package) and, for GestureConfig, the objectui#3363 reclaim note (4 lines); none teaches a renderer or dispatcher reading them and no member carried a published @default. Controls in the same run: triggerIcon 1 (tombstoned on prong 2), striped 19; MobileComponentConfig / MobileOverrides 0 (removed outright). Whether a rename-ledger row or a reclaim note counts as "taught as working" is recorded on objectui#7519 for the docket, not decided in the comment. The objectui#4919 note (the one-line form under correction as objectui#7678) is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract review —
|
| channel | LIVE | DEL (this PR) | TOMB (= never) |
|---|---|---|---|
direct import type + value write |
0 | TS2305 / TS2724 at the import | import silent; TS2322 'never' at the value |
C1 declare module P { interface GestureConfig { meta?: string } } + widened write using meta |
0 | 0 — SILENT | TS2300 Duplicate identifier |
C1b same augmentation, { meta: 1 } |
TS2322 number→string | TS2322 number→string (the augmentation is a real type, not any) |
TS2300 |
C1c same augmentation, old shape { type, action } widened, no overlapping key |
0 | TS2559 (weak-type check — loud by accident of the augmentation being all-optional) | TS2300 |
C2 export * from P compiled alone |
0 | 0 (silent for the re-exporter) | 0 |
C2 downstream import type { GestureConfig } from './mid' |
0 | TS2724 one hop later | TS2322 at the value |
C3 published lib .d.ts inlines import type { GestureConfig } from P, app compiles with skipLibCheck: true (this repo's root default and tsc --init's) |
TS2345 | exit 0 — SILENT: the parameter collapsed to any, bind({ bogus: 1 }) accepted |
TS2345 'never' |
C3 same, skipLibCheck: false |
TS2345 | TS2724 inside the lib .d.ts |
TS2345 |
C3 control: same lib, GestureContext |
TS2345 | TS2345 | TS2345 (the instrument sees real types through the lib) |
C4 JS + JSDoc @type, checkJs: true |
0 | TS2694 | TS2322 |
C4 checkJs: false |
0 | 0 | 0 (nothing is checked) |
C5 type-position-only (cfgs: GestureConfig[], []) |
0 | TS2724 | 0 — SILENT |
C7 import type * as T + T.GestureConfig |
0 | TS2724 | 0 |
Reverse control, the ruling's own premise about a surviving carrier (synthetic local interface Cfg { a?: string }): member deleted + widened value with an overlapping key → 0 (silent) vs tombstoned → TS2322; deleted + lone-key widened value → TS2559 (weak-type check); deleted + fresh literal → TS2353. So the "widened value goes unnoticed" premise holds only when the value shares a key with the carrier; #7669's literal { triggerIcon: 'Sparkles' } alone against the all-optional FloatingChatbotConfig trips TS2559 — a note for #7678's prose, not for this PR.
What the table decides:
- "No channel in which this removal is quiet" — false. C1 and C3 are silent, each with a lit control (C1b, C3-control).
- "The
= neverform is strictly worse than removal" — false. It is louder in C1 and C3 and quieter at the import site and in C5/C7. Neither route dominates; they trade channels. - The precondition sentence — a
?: nevertombstone needs a surviving carrier — is sound as a structural fact, and it does dissolve thecomplex.ts:967/mobile.ts:129contradiction. Its justification clause is what must change. - Route A (remove outright) stands, on substitute grounds: (i) the same-lineage precedent finding(types):
MobileComponentConfigis now a fully inert published type — zero mount points, zero readers #5942 → feat(types,mobile)!: retire the MobileComponentConfig published type #7526, contract-reviewed and merged 2026-09-03, and finding(types):MobileOverrides.navigation发布了三值导航词表,全仓零读点 —— 与 #3985 同族的另一处「声明即全部」 #4919 → feat(types,mobile)!: retire the MobileOverrides type and its mount point (#4919) #5941, both removed outright; a route split between adjacent types in one file is the defect types: the triggerIcon tombstone's rationale contradicts mobile.ts's own retire-vs-remove discriminator, and its tripwire comment overclaims #7678 names; (ii) the direct import is the first-order channel and deletion is the loud route there, while the alias is silent for every type-position-only use (C5, C7); (iii)packages/*/srconorigin/mainhas 0export type X = nevertombstones against a?: nevermember convention in many files — introducing the alias pattern is a package-wide decision, not this card's; (iv) the population that could reach C1/C3 is a strict subset of external consumers, invisible from here and, on every measurement above, likely empty. - Consequence for the record: the types: the triggerIcon tombstone's rationale contradicts mobile.ts's own retire-vs-remove discriminator, and its tripwire comment overclaims #7678 amendment must not be inscribed into
complex.ts:967ormobile.ts:129as written. A wording that survives the table: A?: nevertombstone is available only on a surviving carrier; on one, tombstone when either prong holds. A whole exported type name has no carrier; the only type-level tombstone (export type X = never) trades channels with removal rather than dominating it — louder where a downstream.d.tsor adeclare moduleaugmentation carries the name, quieter at the import and in every type-position-only use — and this package has no alias-tombstone convention. Remove outright, consistent with feat(types,mobile)!: retire the MobileOverrides type and its mount point (#4919) #5941/feat(types,mobile)!: retire the MobileComponentConfig published type #7526, and carry the migration in the changeset. That is a prose follow-up on types: the triggerIcon tombstone's rationale contradicts mobile.ts's own retire-vs-remove discriminator, and its tripwire comment overclaims #7678, which the ruling itself anticipated ("a prose follow-up on types: the triggerIcon tombstone's rationale contradicts mobile.ts's own retire-vs-remove discriminator, and its tripwire comment overclaims #7678, not a hold on this PR"). Nothing merged by this PR states the false universal: themobile.tsnotes say the refusal at the import is loud (true), and the changeset's "no silent-strip hazard" refers to Zod stripping (true). The universal lives in the PR body's Reading A and in the two PM comments.
PR #7683.
needs_decision— resolved by the ruling; the implementation is correct as it stands; no rework.- NOT MEASURED locally, declared:
check:readme-exports— CI "README Export Check" success on the head; downstream type-check sweep — CI "Type Check" success on the head, plus mobiletype-checkexit 0 locally against the rebuilt types dist. - In-flight overlap — not re-scanned by me; measured instead: the two commits
mainis ahead by touch none of the PR's seven files, and feat(types)!: retire FloatingChatbotConfig.triggerIcon as an ADR-0049 tombstone (objectui#7654) #7669's tombstone is absent from the PR's tree (complex.ts:928 triggerIcon?: stringat head vs?: neveronmain) — a merge brings it in with no shared hunk. mergeable_state: behindand draft status — the dispatcher's; CI ran on the merge ref and is green.- Two non-blocking nits, neither parks a PASS: the parity-header sentence in item 8; the changeset's Upgrading section could add one line for consumers who augmented either interface via
declare module(the augmentation now silently declares a fresh type — delete it). The C3 population cannot be reached by a changeset at all; the library author is warned loudly when they rebuild their own.ts.
- Item 1 (contradictory rules in
complex.ts:967vsmobile.ts:129) — dissolved by the carrier precondition; re-word its justification per the paragraph above before inscribing. - Item 2 (the tripwire comment in
floating-chatbot-trigger-icon-retired.test.ts) — out of this PR's scope, untouched, still open there. - The amendment's "one measurement in the body needs correcting" (6 / 10 CHANGELOG lines) — confirmed; and I agree those lines instruct the import in as many words, so prong 2 is met on its literal wording and is not the discriminator.
- Add to that card: the widened-value premise needs the overlapping-key qualifier (reverse control above).
VERDICT: PASS
Generated by Claude Code
Fixes #7519
Retires the
MobileResponsiveConfigandGestureConfigpublished types via the ADR-0049 enforce-or-remove route, following what objectui#5942 (PR #7526) actually did for their only consumer,MobileComponentConfig. This narrows a published surface from two packages, so the PR is a draft carryingneeds:contract-reviewand is for the docket to clear, not for me to land.Dispatched by the
domain:uiPM seat atclaude-fable-5-1under the clause-② contract-review tier mandate; ruling on the card is comment 5548523458. Sessionsession_01KbJQ1y1J12nZxYzFWhP8Q3.Premise re-measured on
origin/mainata472b071before anything was deletedThe card was written before #5942 landed. What landed is the same shape the card predicted: the container is gone (PR #7526, commit
90665e07), and each of the two types is now a declaration plus two barrel re-exports and nothing else.Table 1 — in-repo readers, per type: zero. Word-boundary
git grepover every tracked file onorigin/main(CHANGELOGs and the lockfile excluded), each zero paired with a control lit in the same run.MobileResponsiveConfigmobile.tsdeclaration;types/src/index.tsandmobile/src/index.tsre-exportsResponsiveValue11GestureConfigmobile.tsdeclaration; the same two barrel linesGestureType36,GestureContext7Everything else the bare word hits is prose: doc comments (
mobile.tsGestureType JSDoc, the Spec Touch Vocabulary block,useSpecGesture.tsJSDoc — which names the type only to say it is not the one it reads), the #5942 changeset, and rows/comments inpage-nav-misc-spec-parity.test.ts. No annotation,extends, cast, generic argument, array or import of either name exists outside the two barrels.examples/,apps/,e2e/,skills/,docs/,content/,scripts/and both package READMEs: 0 hits for both (control:useGesture4 hits in the mobile README, 3 in the skills guide).Sibling
objectstackcheckout (5c584231):MobileResponsiveConfig0;GestureConfig5 hits, every one about the spec's own retiredGestureConfig(Schema)(release notes, upgrade guide, migration entries, theinteraction-config-retirement.test.tsledger) — none imports from@object-ui/*; nopackage.jsonin that repo depends on@object-ui/typesor@object-ui/mobileat all (control:@objectstack/specin 73 package.json files;ComponentSchema270 hits).Table 2 — published-surface reachability: both names ship from two packages. Measured on the built dist at the BASE commit (fresh worktree, so no stale dist could exist):
.d.tsoccurrences (word-boundary)MobileResponsiveConfigGestureConfigGestureContextpackages/types/distpackages/mobile/distuseSpecGesture.d.tsdoc mention)Both packages are public (
17.6.0), exports map"."points at./dist/index.d.ts, and both barrels re-export the names one hop from the root. Clause-② is yes on my own reading too: a TypeScript consumer of either name from either package breaks.Route: deletion, because that is what #5942 did — and a tombstone has nowhere to live here
packages/types/src/zod/has no mobile twin and its index never names either type; neither shape was ever parsed by any mirror. So the "deletion under a non-strictz.objectis a silent strip, a tombstone is a named refusal" argument has no schema to apply to — there is no twin to host aretirementTombstone(), and the refusal can only be type-level. That is the objectui#7654 shape, and it is why the pin below istsc-only rather than a shortcut.?: never, the route finding(types):MobileComponentConfigis now a fully inert published type — zero mount points, zero readers #5942 and finding(types):MobileOverrides.navigation发布了三值导航词表,全仓零读点 —— 与 #3985 同族的另一处「声明即全部」 #4919 took: whole interfaces go, so there is no surviving object to hang aneverkey on, andSpecGestureConfigis not a successor ofGestureConfig(different contract: a tuning record with noaction). Whether the two-prong discriminator licenses this for these two names is measured in the next section — it is the open question of this PR.//RETIRED note — a line comment, so declaration emit strips it — and the surviving JSDoc onGestureTypeand onuseSpecGestureno longer names the retired symbol. That restores the property feat(types,mobile)!: retire the MobileComponentConfig published type #7526's contract-review round established: both names occur zero times across every emitted.d.tsafter rebuild (grep -rhow NAME packages/*/dist --include='*.d.ts'= 0 / 0; controlGestureContext2 / 3, unchanged). The oneRETIREDstring that IS emitted fromdist/mobile.d.tsis the pre-existingRETIRED_DEFS_BY_MAJOR[18]identifier in the BreakpointName JSDoc at BASE — a real reference, not a dangling pointer.useResponsive/ResponsiveContainer/useBreakpointanduseGesture({ type, onGesture, threshold?, longPressDuration?, enabled? }) are untouched;ResponsiveValue,BreakpointName,GestureType,GestureContextstay exported from both packages.Two-prong discriminator, measured per type (PM correction, objectui#7678)
The discriminator, quoted from the #5941 / #7526 changesets: a tombstone exists to steer authors to a named live replacement KEY, or to keep loud a key the docs taught as working.
mobile.ts's one-line form in the #4919 note (the card'smobile.ts:129; that note starts at line 119 at BASE and at line 122 after this diff — the note itself is untouched, its correction is #7678's) is not relied on.Prong 1 — a named live replacement key: none, for either.
git greponorigin/main, exit captured before any pipe)MobileResponsiveConfigstackOnMobile/showOnly/stackBreakpointhave 0 readers outside the declaration (control:columns3686 lines, same shape); no mounted type has carried aresponsivemember since #5942. The behaviour lives in hooks (useResponsive/ResponsiveContainer/useBreakpoint), which are not keys.GestureConfigaction(0 hits); the onlygestureskey on any type isTouchInteraction.gestures: SpecGestureConfig[]— a different contract, andTouchInteractionitself has no reader outside its declaration and barrels.SpecGestureConfigis not a successor.Prong 2 — advertised as working (release-note/CHANGELOG line, or a published JSDoc
@default): CHANGELOG lines exist for both, and every one is a name-ledger entry. Same query shape (word-boundary overCHANGELOG.md+ everypackages/*/CHANGELOG.md,origin/main) applied to the two names, to the precedents, and to the controls:@default/@exampleon the declarationMobileResponsiveConfigResponsiveConfig→MobileResponsiveConfig· "mobile box config, not the spec's SDUI grid contract"), replicated into six package CHANGELOGs because the fixed group publishes one changeset text everywhereGestureConfigGestureConfig→TouchGestureConfig· "gesture→actionbinding, not per-gesture tuning") + the objectui#3363 reclaim note in two packages ("Reclaim the natural namesGestureTypeandGestureConfig" / "TouchGestureConfig→GestureConfig— the flat gesture→actionhandler binding … Nothing about either shape changed")triggerIcon(tombstoned by #7654 on prong 2)CHANGELOG.md:578, 3.3.0 "New …triggerIcon… options")@default 'MessageCircle'(lit)striped(tombstoned, prong 1)MobileComponentConfig(removed outright, #7526)MobileOverrides/mobileOverrides(removed outright, #5941)So the reading is not "neither prong, cleanly", which is the sentence the PM asked me to write only if it were true. It is: prong 1 no; prong 2 — CHANGELOG lines exist, all of them record a name (a rename, then a reclaim) and none records behaviour (no renderer or dispatcher named as reading the type, no
@example, no@default), which is a case the two-prong wording does not classify, and one the precedents did not face (their counts were 0).Two readings, two routes:
z.objecta deleted key is stripped, and only a tombstone turns that into a named refusal. Nothing here can be silent — a whole unmirrored, unmounted type is never parsed, and its deletion is already the compiler's own loud refusal (TS2305 / TS2724, measured above). The rename rows and the reclaim note told readers the name, not that anything read it. Under A the licensed route is remove outright, exactly as implemented, and the same route the identical lineage (MobileComponentConfig) took.neveraliases with a JSDoc migration note (export type GestureConfig = never), from both barrels, indefinitely. That keeps the names on the published surface, gives a generic "not assignable to type never" at the use site (the note reaches only hover /.d.tsreaders), and contradicts the finding(types):MobileComponentConfigis now a fully inert published type — zero mount points, zero readers #5942 precedent for the same lineage. It is also a new tombstone pattern, which is a package-wide decision rather than this card's.My recommendation is A — the measurement shows name-ledger lines and nothing that promised behaviour, and the hazard prong 2 exists for cannot occur here — but the PM's instruction was explicit that this is not mine to default, so the PR reports
needs_decisionand stays a draft. If B is chosen, this PR is reworked (the pin test'stscleg inverts: the names must resolve tonever), not patched.The loud-signal requirement, measured with a real external consumer on both legs
A tiny consumer package (its own directory,
node_modules/@object-ui/*symlinked to the worktree packages,moduleResolution: bundler) compiled one probe file per name per package against the built.d.tsthrough the real exports map;--traceResolutionconfirms both control files resolve topackages/types/dist/index.d.tsandpackages/mobile/dist/index.d.ts. Each package was rebuilt before the after-leg reading.import type { MobileResponsiveConfig } from '@object-ui/types'import type { GestureConfig } from '@object-ui/types'Did you mean 'SpecGestureConfig'?@object-ui/mobileGestureContext+GestureTypefrom types;GestureContext+ResponsiveValuefrom mobileThe before column is the card's defect reproduced: the dead names import cleanly from both published packages. The compiler's
SpecGestureConfigsuggestion is a lexical near-match, not a migration target, and the changeset says so.The tripwire pins: retired with the types, and the absence pinned instead
page-nav-misc-spec-parity.test.tscarried three rows on these names (['ResponsiveConfig','MobileResponsiveConfig'],['GestureConfig','TouchGestureConfig'], and theMobileResponsiveConfig"spec does not own" row). The card asked what happens to them. All three would have stayed green after the deletion — they assert facts about the spec's export set, not about this package — which is exactly the phantom-check shape: a pin on a name this package no longer exports guards nothing, because the spec re-publishingGestureConfigorResponsiveConfigwould collide with no export here. The rows are removed and the file's history header records why, the way it records each earlier tripwire firing; theGestureTyperow stays (live, read by two hooks).The forward-looking guard is the new
mobile-residue-retired-7519.test.ts:tscchannel (instrument:tsc -p packages/types/tsconfig.test.json; program membership proven with--listFiles, which lists the file and readssrc/mobile.ts/src/index.tsdirectly — 0 dist files in the program): four@ts-expect-errorlines through theimport('../index.js').NAME/import('../mobile.js').NAMEspelling, each on a line whose only possible diagnostic is the missing export (the value is used, the literal would type-check cleanly if the declaration came back), and each paired with a live name reached through the identical spelling with no directive so a broken specifier turns the control red rather than satisfying the directive by TS2307.OfflineConfigpin) and asserted to no longer carry the names whileGestureContext/ResponsiveValue/GestureTypestill do, plus one//RETIRED note per name. This is the leg that reaches the@object-ui/mobilebarrel, which the compiler leg cannot from this package.Reverse verification (after commit; restore under an
EXIT INT TERMtrap with absolute paths)Prediction written before the leg, then the three source files (
mobile.tsand both barrels) were put back to their BASE content withgit checkout BASE -- ABSOLUTE_PATHS. Mutation proven on disk by anchored counts (export interface MobileResponsiveConfig0→1,export interface GestureConfig {0→1,// RETIRED (objectui#75192→0, barrel lines 0→1 in both barrels) and blob movement (each file'sgit hash-object== its BASE blob and != its HEAD blob; an empty hash is coded as failure). No rebuild was needed on either leg: both instruments readsrc/, neverdist/.tsc -p tsconfig.test.jsonon the mutated treeRestore:
git checkout HEAD -- ABSOLUTE_PATHSfrom the trap, then proven by state, not by exit code —git diff HEADempty,git status --porcelainempty (index included), all three blobs equal their HEAD blobs, anchors back at 2 / 0 / 0 / 0 / 0.Verification union — run at
45df4a2ecafter the final commit, each verdict quoted from the gate's own outputpnpm exec vitest run --maxWorkers=2 packages/types/ packages/mobile/(repo root, under the shared verify lock):Test Files 107 passed (107)·Tests 1757 passed (1757); the new file alone with--reporter=verbose(at125643e5b, the commit that introduced it): 6 / 6, andpage-nav-misc-spec-parity.test.tsstill green. The second commit45df4a2ecchanges//comments and the changeset only; the union, the dist counts (0 / 0, control 5) and the consumer probe were re-run on it.tsc -p packages/types/tsconfig.test.json --listFiles: exit 0, 0 diagnostics, new file listed (104 test files in the program).pnpm --filter @object-ui/types type-checkandpnpm --filter @object-ui/mobile type-check(the latter readspackages/types/dist, rebuilt first): exit 0 / 0.pnpm --filter @object-ui/types buildandpnpm --filter @object-ui/mobile build:✓ dist completeness: 1 package(s) completefor each (120 / 48 emitted files).pnpm --filter @object-ui/types lint/@object-ui/mobile lint: exit 0 / 0, no message on any touched file (pre-existingno-explicit-anywarnings only, elsewhere).check-changeset-presence(✅ 5 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)),check-changeset-no-major(✅ No changeset declares a major bump),check-changeset-overwrite(✅ No pre-existing changeset was modified or deleted).check:control-bytes,check:spec-symbols,check:unreferenced-sources(OK Every shipped source file in every covered package is reachable),check:published-tsconfig-exclude(OK -- all 34 enforced package(s)),check:phantom-deps(✅ Every in-scope import is declared),check:side-effects-array: all exit 0.NOT MEASURED locally, declared, left to CI:
check:readme-exportscollapsed on34 unbuiltpackages (the population COLLAPSED -- this run proves nothing; PRECONDITION NOT MET — it needs the full-repo build CI performs). The narrowing evidence: neither package README mentions either name (0 / 0, controluseGesture4). Likewise the full downstreamtype-checksweep of every@object-ui/typesconsumer (pnpm --filter '...@object-ui/types' type-check, prefix form = downstream) is CI's run; the whole-tree word-boundary grep above (controls lit) is why a zero there is expected, and the one in-repo package that re-exported the names,@object-ui/mobile, was type-checked against the rebuilt dist.Scope fence, declared honestly
The fence was
packages/types/src/mobile.tsand the two barrels. Three further files are touched, and each is inside this retirement's own blast radius rather than a cascade:packages/mobile/src/useSpecGesture.ts— JSDoc only. Its doc namedGestureConfigto say "not this package's ownGestureConfig"; that sentence is emitted intopackages/mobile/dist/useSpecGesture.d.ts, so leaving it would ship a dangling reference to a removed name — the exact defect feat(types,mobile)!: retire the MobileComponentConfig published type #7526's contract-review round failed on. No in-flight branch touches this file (scan below).packages/types/src/__tests__/page-nav-misc-spec-parity.test.ts— the three rows the card itself asked about (disposition above).packages/types/src/__tests__/mobile-residue-retired-7519.test.ts— new retirement pin.⛔ Not touched:
registry.ts,complex.ts,ChartDataSeriesSchema. No further zero-consumer types were found one level down:ResponsiveValue,GestureType,GestureContextare all read by hooks (counts above), so nothing to file.In-flight overlap scan (every
claude/*branch on the remote naming mobile / gesture / responsive / 7654):issue-7654-trigger-icon-retiretouches onlycomplex.ts+ its own test and changeset;issue-6856-mobile-guide-useoffline-snippetis an empty probe branch atmain;issue-4773-responsive-vocabulary-livenessis a stale pointer at an oldmaincommit with no unique work. No file overlap with this PR.Blast radius, stated plainly
The in-repo consumer count is zero, and that is "no consumers in this repo", not "no consumers". Both names shipped from two public packages for several releases; an out-of-repo consumer importing either from either package is invisible from here, which is why the changeset is graded
minoron the published-surface change (this repo's version-alignment rule reservesmajorfor following@objectstackacross a major, the same classification #5942 and #4919 used) and carries the upgrade note, the hook pointers, and the warning against the compiler'sSpecGestureConfigsuggestion.Clause-② status
needs:contract-reviewadded to the card (additive endpoint, compare-read-back: union complete, nothing stripped) and to this PR.Generated by Claude Code